package com.jwcao.common.cast.operation.access.field;

import com.jwcao.common.cast.annotation.CastField;
import com.jwcao.common.cast.annotation.factory.CastFieldFactory;
import com.jwcao.common.cast.annotation.CastIgnore;
import com.jwcao.common.cast.operation.access.AccessOperation;
import com.jwcao.common.cast.operation.access.field.collection.ListFieldAccessOperation;
import com.jwcao.common.scan.object.ScanClass;
import com.jwcao.common.scan.object.ScanField;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * name: FieldAccessOperationFactory description: TODO
 *
 * @author caojingwei date: 2023/3/24 13:52
 */
public class FieldAccessOperationFactory {

    public static List<AccessOperation> get(String id, ScanClass sourceClass, ScanClass targetClass) {
        List<ScanField> sourceFieldList = sourceClass.getScanFieldList();

        return sourceFieldList.stream().map(sourceField -> {

            CastIgnore[] castIgnores = sourceField.getField().getAnnotationsByType(CastIgnore.class);

            // 表示忽略该字段
            boolean isIgnore = Arrays.stream(castIgnores)
                                     .anyMatch(castIgnore -> StringUtils.equals(castIgnore.id(), id));

            if (isIgnore) {
                return getIgnoreField(sourceField);
            }
            return getField(id, sourceField, targetClass);

        }).collect(Collectors.toList());

    }

    public static IgnoreFieldAccessOperation getIgnoreField(ScanField sourceField) {
        return new IgnoreFieldAccessOperation(null, sourceField);
    }

    public static CastFieldAccessOperation getField(String id, ScanField sourceField, ScanClass targetClass) {
        String name = sourceField.getName();
        CastField castField = CastFieldFactory.get(id, sourceField.getField());
        if (castField != null && StringUtils.isNotBlank(castField.value())) {
            name = castField.value();
        }
        ScanField targetField = targetClass.getField(name);

        if (targetField == null) {
            throw new RuntimeException(
                    "target field not found! id：" + id + ",source field：" + sourceField.getName() + ", targetClass: "
                    + targetClass.getClazz().getName());
        }

        if (Collection.class.isAssignableFrom(sourceField.getFieldType()) &&
            Collection.class.isAssignableFrom(targetField.getFieldType())) {

//            ParameterizedType aType = (ParameterizedType) sourceField.getField().getGenericType();
//            //TODO:: 如果是 List 嵌套 List 的情况，这里返回结果不是 class 可能是其他 Type 需要处理
//            Class sourceItemClass = (Class) aType.getActualTypeArguments()[0];
            Class sourceItemClass = getActualType(sourceField.getField().getGenericType());
//            Type sourceItemType = aType.getActualTypeArguments()[0];
//            if (sourceItemType instanceof ParameterizedType sourceItemTypeParameterizedType){
//                sourceItemTypeParameterizedType.getActualTypeArguments().getClass()[0]
//            }

//            ParameterizedType bType = (ParameterizedType) targetField.getField().getGenericType();
//            Class targetItemClass = (Class) bType.getActualTypeArguments()[0];
            Class targetItemClass = getActualType(targetField.getField().getGenericType());
            return new ListFieldAccessOperation(castField, sourceField, targetField, sourceItemClass,
                                                targetItemClass);
        }
        return new CastFieldAccessOperation(castField, sourceField, targetField);
    }

    public static Class getActualType(Type type) {
        if (type instanceof ParameterizedType parameterizedType) {
            return getActualType(parameterizedType.getActualTypeArguments()[0]);
        }
        if (type instanceof TypeVariable typeVariable){
            typeVariable.
        }
        return (Class) type;
    }


}
